﻿using System;
using System.Text;
using System.Runtime.InteropServices;
namespace Cabinink.DataTreatment.DocumentData
{
   /// <summary>
   /// 配置文件（后缀名为*.ini）操作类。
   /// </summary>
   [Serializable]
   [ComVisible(true)]
   public class InitializationFileOperator
   {
      /// <summary>
      /// 获取所有节点名称(Section)。
      /// </summary>
      /// <param name="sectionNameBuffer">存放节点名称的内存地址,每个节点之间用\0分隔。</param>
      /// <param name="size">内存大小(characters)。</param>
      /// <param name="fileUrl">指定的Ini文件。</param>
      /// <returns>内容的实际长度,为0表示没有内容,为nSize-2表示内存大小不够。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      private static extern uint GetPrivateProfileSectionNames(IntPtr sectionNameBuffer, uint size, string fileUrl);
      /// <summary>
      /// 获取某个指定节点(Section)中所有KEY和Value。
      /// </summary>
      /// <param name="section">节点名称。</param>
      /// <param name="resultMemoryAddress">返回值的内存地址,每个之间用\0分隔。</param>
      /// <param name="size">内存大小(characters)。</param>
      /// <param name="fileUrl">Ini文件。</param>
      /// <returns>内容的实际长度,为0表示没有内容,为nSize-2表示内存大小不够。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      private static extern uint GetPrivateProfileSection(string section, IntPtr resultMemoryAddress, uint size, string fileUrl);
      /// <summary>
      /// 读取INI文件中指定的Key的值。
      /// </summary>
      /// <param name="section">节点名称。如果为null,则读取INI中所有节点名称,每个节点名称之间用\0分隔。</param>
      /// <param name="key">Key名称。如果为null,则读取INI中指定节点中的所有KEY,每个KEY之间用\0分隔。</param>
      /// <param name="defaultValue">读取失败时的默认值。</param>
      /// <param name="buffer">读取的内容缓冲区，读取之后，多余的地方使用\0填充。</param>
      /// <param name="size">内容缓冲区的长度。</param>
      /// <param name="fileUrl">INI文件名。</param>
      /// <returns>实际读取到的长度。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      private static extern uint GetPrivateProfileString(string section, string key, string defaultValue, [In, Out] char[] buffer, uint size, string fileUrl);
      /// <summary>
      /// 读取INI文件中指定的Key的值。
      /// </summary>
      /// <param name="section">节点名称。如果为null,则读取INI中所有节点名称,每个节点名称之间用\0分隔。</param>
      /// <param name="key">Key名称。如果为null,则读取INI中指定节点中的所有KEY,每个KEY之间用\0分隔。</param>
      /// <param name="defaultValue">读取失败时的默认值。</param>
      /// <param name="buffer">读取的内容缓冲区。</param>
      /// <param name="size">内容缓冲区的长度。</param>
      /// <param name="fileUrl">INI文件名。</param>
      /// <returns>实际读取到的长度。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      private static extern uint GetPrivateProfileString(string section, string key, string defaultValue, StringBuilder buffer, uint size, string fileUrl);
      /// <summary>
      /// 读取INI文件中指定的Key的值。
      /// </summary>
      /// <param name="section">节点名称。如果为null,则读取INI中所有节点名称,每个节点名称之间用\0分隔。</param>
      /// <param name="key">Key名称。如果为null,则读取INI中指定节点中的所有KEY,每个KEY之间用\0分隔。</param>
      /// <param name="defaultValue">读取失败时的默认值。</param>
      /// <param name="buffer">读取的内容缓冲区。</param>
      /// <param name="size">内容缓冲区的长度。</param>
      /// <param name="fileUrl">INI文件名。</param>
      /// <returns>实际读取到的长度。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      private static extern uint GetPrivateProfileString(string section, string key, string defaultValue, string buffer, uint size, string fileUrl);
      /// <summary>
      /// 将指定的键值对写到指定的节点，如果已经存在则替换。
      /// </summary>
      /// <param name="section">节点，如果不存在此节点，则创建此节点。</param>
      /// <param name="dictionary">Item键值对，多个用\0分隔,形如key1=value1\0key2=value2。
      /// <para>如果为string.Empty，则删除指定节点下的所有内容，保留节点。</para>
      /// <para>如果为null，则删除指定节点下的所有内容，并且删除该节点。</para>
      /// </param>
      /// <param name="fileUrl">INI文件。</param>
      /// <returns>是否成功写入。</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool WritePrivateProfileSection(string section, string dictionary, string fileUrl);
      /// <summary>
      /// 将指定的键和值写到指定的节点，如果已经存在则替换。
      /// </summary>
      /// <param name="section">节点名称</param>
      /// <param name="key">键名称。如果为null，则删除指定的节点及其所有的项目</param>
      /// <param name="valueContext">值内容。如果为null，则删除指定节点中指定的键。</param>
      /// <param name="fileUrl">INI文件</param>
      /// <returns>操作是否成功</returns>
      [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      private static extern bool WritePrivateProfileString(string section, string key, string valueContext, string fileUrl);
      /// <summary>
      /// 读取INI文件中指定INI文件中的所有节点名称(Section)。
      /// </summary>
      /// <param name="fileUrl">Ini文件。</param>
      /// <returns>所有节点,没有内容返回string[0]。</returns>
      public static string[] GetAllSectionNames(string fileUrl)
      {
         uint max_buffer = 32767;
         string[] sections = new string[0];
         IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)max_buffer * sizeof(char));
         uint bytesReturned = GetPrivateProfileSectionNames(pReturnedString, max_buffer, fileUrl);
         if (bytesReturned != 0)
         {
            string local = Marshal.PtrToStringAuto(pReturnedString, (int)bytesReturned).ToString();
            sections = local.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
         }
         Marshal.FreeCoTaskMem(pReturnedString);
         return sections;
      }
      /// <summary>
      /// 获取INI文件中指定节点(Section)中的所有条目(key=value形式)。
      /// </summary>
      /// <param name="fileUrl">Ini文件。</param>
      /// <param name="section">节点名称。</param>
      /// <returns>指定节点中的所有项目,没有内容返回string[0]。</returns>
      public static string[] GetAllItems(string fileUrl, string section)
      {
         uint max_buffer = 32767;
         string[] items = new string[0];
         IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)max_buffer * sizeof(char));
         uint bytesReturned = GetPrivateProfileSection(section, pReturnedString, max_buffer, fileUrl);
         if (!(bytesReturned == max_buffer - 2) || (bytesReturned == 0))
         {
            string returnedString = Marshal.PtrToStringAuto(pReturnedString, (int)bytesReturned);
            items = returnedString.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
         }
         Marshal.FreeCoTaskMem(pReturnedString);
         return items;
      }
      /// <summary>
      /// 获取INI文件中指定节点(Section)中的所有条目的Key列表。
      /// </summary>
      /// <param name="fileUrl">Ini文件。</param>
      /// <param name="section">节点名称。</param>
      /// <returns>如果没有内容,反回string[0]。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static string[] GetAllItemKeys(string fileUrl, string section)
      {
         string[] value = new string[0];
         const int SIZE = 1024 * 10;
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         char[] chars = new char[SIZE];
         uint bytesReturned = GetPrivateProfileString(section, null, null, chars, SIZE, fileUrl);
         if (bytesReturned != 0)
         {
            value = new string(chars).Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
         }
         chars = null;
         return value;
      }
      /// <summary>
      /// 读取INI文件中指定KEY的字符串型值。
      /// </summary>
      /// <param name="fileUrl">Ini文件。</param>
      /// <param name="section">节点名称。</param>
      /// <param name="key">键名称。</param>
      /// <param name="defaultValue">如果没此KEY所使用的默认值。</param>
      /// <returns>读取到的值。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static string GetStringValue(string fileUrl, string section, string key, string defaultValue)
      {
         string value = defaultValue;
         const int size = 1024 * 10;
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         if (string.IsNullOrEmpty(key)) throw new ArgumentException("必须指定键名称(key)", "key");
         StringBuilder sb = new StringBuilder(size);
         uint bytesReturned = GetPrivateProfileString(section, key, defaultValue, sb, size, fileUrl);
         if (bytesReturned != 0) value = sb.ToString();
         sb = null;
         return value;
      }
      /// <summary>
      /// 在INI文件中，将指定的键值对写到指定的节点，如果已经存在则替换。
      /// </summary>
      /// <param name="fileUrl">INI文件。</param>
      /// <param name="section">节点，如果不存在此节点，则创建此节点。</param>
      /// <param name="items">键值对，多个用\0分隔,形如key1=value1\0key2=value2。</param>
      /// <returns>指示是否已经写入。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static bool WriteItems(string fileUrl, string section, string items)
      {
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         if (string.IsNullOrEmpty(items)) throw new ArgumentException("必须指定键值对", "items");
         return WritePrivateProfileSection(section, items, fileUrl);
      }
      /// <summary>
      /// 在INI文件中，指定节点写入指定的键及值。如果已经存在，则替换。如果没有则创建。
      /// </summary>
      /// <param name="fileUrl">INI文件。</param>
      /// <param name="section">指定的节点。</param>
      /// <param name="key">指定的键。</param>
      /// <param name="context">写入的值。</param>
      /// <returns>操作是否成功。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static bool WriteValue(string fileUrl, string section, string key, string context)
      {
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         if (string.IsNullOrEmpty(key)) throw new ArgumentException("必须指定键名称", "key");
         if (context == null) throw new ArgumentException("值不能为null", "context");
         return WritePrivateProfileString(section, key, context, fileUrl);
      }
      /// <summary>
      /// 在INI文件中，删除指定节点中的指定的键。
      /// </summary>
      /// <param name="fileUrl">INI文件。</param>
      /// <param name="section">节点。</param>
      /// <param name="key">指定的键。</param>
      /// <returns>操作是否成功。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static bool DeleteKey(string fileUrl, string section, string key)
      {
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         if (string.IsNullOrEmpty(key)) throw new ArgumentException("必须指定键名称", "key");
         return WritePrivateProfileString(section, key, null, fileUrl);
      }
      /// <summary>
      /// 在INI文件中，删除指定的节点。
      /// </summary>
      /// <param name="fileUrl">INI文件。</param>
      /// <param name="section">节点。</param>
      /// <returns>操作是否成功。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static bool DeleteSection(string fileUrl, string section)
      {
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         return WritePrivateProfileString(section, null, null, fileUrl);
      }
      /// <summary>
      /// 在INI文件中，删除指定节点中的所有内容。
      /// </summary>
      /// <param name="fileUrl">INI文件。</param>
      /// <param name="section">节点。</param>
      /// <returns>操作是否成功。</returns>
      /// <exception cref="ArgumentException">当出现非法的函数参数传递（比如节点名称错误等等）时，则会抛出这个异常。</exception>
      public static bool EmptySection(string fileUrl, string section)
      {
         if (string.IsNullOrEmpty(section)) throw new ArgumentException("必须指定节点名称", "section");
         return WritePrivateProfileSection(section, string.Empty, fileUrl);
      }
   }
}
